<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Mootools - Sortable order example</title>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<style type="text/css">
ul#order {
	margin: 0;
	padding: 0;
	list-style: none;
	width: 400px;

}
ul#order li {
	float:left;
}
.handle {
	width:200px;
	background-color: #eee;
	margin: 0;
	padding: 10px 0;
	cursor: move;
}
#s1 {background-color: #FF0000}
#s2 {background-color: #00CC00}
#s3 {background-color: #0033FF}
#s4 {background-color: #9933FF}
#s5 {background-color: #FFFF00}
#s6 {background-color: #FF6600}
</style>
<script src="http://chatalors.chez-alice.fr/mootools.release.64.js" type="text/javascript"></script>
<script type="text/javascript">
/*
* class SortableOrder (rewrite of Sortables.js from mootools.release.64.js to manage 2-dimensional sortable list)
* Depends on Moo.js + Native Scripts + DragDrop.js + Fx.js
*/
var SortableOrder = new Class({

	setOptions: function(options) {
		this.options = {
			handles: false,
			fxDuration: 200,
			fxTransition: Fx.sinoidal,
			copyClassName: 'handle'
		};
		Object.extend(this.options, options || {});
	},

	initialize: function(elements, options){
		this.setOptions(options);
		this.options.handles = this.options.handles || elements;
		var trash = new Element('div').injectInside($(document.body));

		// a position index must be set for each element of the list (see el.position = i)
		// updateOrder is used in onDrag to update position index of each element of the list
		function updateOrder(elements, fromPosition, toPosition){
			elements.each(function(el){
				if (el.position == fromPosition){
					 el.position = toPosition;
				} else if (el.position > fromPosition && el.position <= toPosition){
					el.position--;
				} else if (el.position < fromPosition && el.position >= toPosition){
					el.position++;
				}
			});
		}
		$A(elements).each(function(el, i){
			el.position = i;
			dropElements = $A(elements);
			dropElements.remove(el);
			var dropElementsOptions = {
				onOver: function(draggedElement){
					draggedElement.dropIn = this;
				}
			}
			dropElements.each(function(drop){drop.extend(dropElementsOptions)});

			var copy = $(el).clone().addClassName(this.options.copyClassName).setStyles({'position': 'absolute', 'display': 'none'}).injectInside(trash);
			
			var copyEffects = copy.effects({
				duration: this.options.fxDuration,
				wait: false,
				transition: this.options.fxTransition,
				onComplete: function(){
					copy.setStyle('display', 'none');
					el.setStyle('visibility', 'visible');
				}
			});
			var dragger = new Drag.Move(copy, {
				snap: false,
				droppables: dropElements,
				onStart: function(){
					copy.setHTML(el.innerHTML).setStyles({
						'display': 'block',
						'background-color': el.getStyle('background-color'),
						'top': el.getTop() +'px',
						'left': el.getLeft() +'px'
					});
					el.setStyle('visibility', 'hidden');
				}.bind(this),
				onComplete: function(){
					copyEffects.custom({'top': [copy.getTop(), el.getTop()], 'left': [copy.getLeft(), el.getLeft()]});
				}.bind(this),
				onDrag: function(draggedElement){
					if (draggedElement.dropIn != null && draggedElement.dropIn.position != el.position){
						if (el.position < draggedElement.dropIn.position){
							(draggedElement.dropIn.getNext())? el.parentNode.insertBefore(el, draggedElement.dropIn.getNext()) : el.parentNode.insertBefore(el, null);
						} else if (el.position > draggedElement.dropIn.position) {
							el.parentNode.insertBefore(el, draggedElement.dropIn);
						}
						updateOrder(elements, el.position, draggedElement.dropIn.position);
						draggedElement.dropIn = null;
					}
				}
			});
			this.options.handles[i].onmousedown = dragger.start.bind(dragger);
		}, this);

	}
});

window.onload = function(){
	new SortableOrder($S('.handle'));
}

/*
* Mootools update (based on mootools.release.64.js -> DragDrop.js)
*/
Drag.Base.implement({
	set: function(evt){
		evt = evt || window.event; // added - prevend IE firing error with evt : clientX is null or not an object
		this.lastMouseX = evt.clientX;
		this.lastMouseY = evt.clientY;
		return false;
	}
});

/*
* IE 5.5 issue with Window methods, added: document.body (overwrite Window methods from mootools.release.64.js)
*/

Window.extend({
	getWidth: function(){
		return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0;
	},
	
	getHeight: function(){
		return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0;
	},
	getScrollHeight: function(){
		return document.documentElement.scrollHeight || document.body.scrollHeight;
	},
	
	getScrollWidth: function(){
		return document.documentElement.scrollWidth || document.body.scrollWidth;
	},
	getScrollTop: function(){
		return document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop || 0;
	},
	getScrollLeft: function(){
		return document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft || 0;
	}
});
</script>
</head>
<body>
<ul id="order">
<li id="s1" class="handle">s1</li>

<li id="s2" class="handle">s2</li>
<li id="s3" class="handle">s3</li>
<li id="s4" class="handle">s4</li>
<li id="s5" class="handle">s5</li>
<li id="s6" class="handle">s6</li>
</ul>
</body>
</html>
